#ifndef WINSTJACKONW_STACKQUEUE_HPP__
#define WINSTJACKONW_STACKQUEUE_HPP__
#include <atomic>
#include <cstddef>
#include <new>
#include <optional>
#include <ranges>
#include <thread>
namespace WinstJackonw {
constexpr auto CACHELINE_SIZE = std::hardware_constructive_interference_size;
constexpr auto DEFAULT_CAPACITY = 64;
// constexpr bool ENABLE_WAIT = true;

template <typename T, std::size_t sizeOfArray = DEFAULT_CAPACITY>
  requires std::is_nothrow_move_constructible_v<T> &&
           std::is_destructible_v<T> && ((sizeOfArray & (sizeOfArray - 1)) == 0)
class SPSCRingBuffer {
 public:
  constexpr auto capacity() { return sizeOfArray - 1; }
  SPSCRingBuffer() noexcept : front_(0), rear_(sizeOfArray - 1), data_() {}
  ~SPSCRingBuffer() {
    while (!IsEmpty()) {
      Pop();
    }
  }
  SPSCRingBuffer(const SPSCRingBuffer&) = delete;
  SPSCRingBuffer& operator=(const SPSCRingBuffer&) = delete;
  SPSCRingBuffer(SPSCRingBuffer&&) = delete;
  SPSCRingBuffer& operator=(SPSCRingBuffer&&) = delete;
  bool Push(T rhs) noexcept {
    const auto posToWrite = GetWritePos(std::memory_order_relaxed);
    // equals to (GetWritePos() + 1) & (capacity - 1)
    const auto nextWritePos = (posToWrite + 1) % sizeOfArray;
    const auto currentFront =
        front_.load(std::memory_order_acquire) % sizeOfArray;
    if (nextWritePos == currentFront) return false;
    new (&data_[posToWrite].value) T{(T&&)rhs};
    rear_.store(posToWrite, std::memory_order_release);
    // if constexpr (ENABLE_WAIT) {
    //   rear_.notify_one();
    // }
    return true;
  }
  std::optional<T> Pop() noexcept {
    const auto posToRead = front_.load(std::memory_order_relaxed) % sizeOfArray;
    const auto currentRear = GetWritePos(std::memory_order_acquire);
    if (posToRead == currentRear) return std::nullopt;
    // NOLINTNEXTLINE
    auto pRes = reinterpret_cast<T*>(&data_[posToRead].value);
    T res{(T&&)*pRes};
    pRes->~T();
    front_.store(posToRead + 1, std::memory_order_release);
    return res;
  }

  // void WaitForData() {
  //   if constexpr (ENABLE_WAIT) {
  //     if (IsEmpty()) rear_.wait(rear_.load());
  //   } else {
  //     std::this_thread::yield();
  //   }
  // }
  bool IsFull() const noexcept {
    const auto nextWritePos =
        (GetWritePos(std::memory_order_relaxed) + 1) % sizeOfArray;
    const auto currentFront =
        front_.load(std::memory_order_relaxed) % sizeOfArray;
    return nextWritePos == currentFront;
  }

  bool IsEmpty() const noexcept {
    const auto posToRead = front_.load(std::memory_order_relaxed) % sizeOfArray;
    const auto currentRear = GetWritePos(std::memory_order_relaxed);
    return posToRead == currentRear;
  }

 private:
  alignas(CACHELINE_SIZE) std::atomic_size_t front_;
  alignas(CACHELINE_SIZE) std::atomic_size_t rear_;
  auto GetWritePos(
      std::memory_order mem_ord = std::memory_order_seq_cst) const noexcept {
    return (rear_.load(mem_ord) + 1) % sizeOfArray;
  }
  struct SharingStruct {
    T value;
    // NOLINTNEXTLINE
    std::byte
        padding[sizeof(T) > CACHELINE_SIZE ? 0 : (CACHELINE_SIZE - sizeof(T))];
  };
  // NOLINTNEXTLINE
  alignas(CACHELINE_SIZE) SharingStruct data_[sizeOfArray];
};
namespace trash {
constexpr auto DEFAULT_QUEUE_SIZE = 512;
using MyQueue = SPSCRingBuffer<char, DEFAULT_QUEUE_SIZE>;
class MyStack {
 public:
  void Push(char c) noexcept {
    queue_.Push(c);
    size_++;
    for (auto _ : std::ranges::views::iota(std::size_t{0}, size_ - 1)) {
      queue_.Push(queue_.Pop().value());
    }
  }
  std::optional<char> Pop() noexcept { return queue_.Pop(); }

 private:
  std::size_t size_{};
  MyQueue queue_;
};
}  // namespace trash

}  // namespace WinstJackonw
#endif  // WINSTJACKONW_STACKQUEUE_HPP__
